Conversation
There was a problem hiding this comment.
Pull Request Overview
This pull request implements a comprehensive migration from a file-based session storage system to an SQLite database for session management. The primary purpose is to replace the custom JSONL file-based session storage with a more robust, standardized database approach.
Key changes:
- Replace custom JSONL session storage with SQLite database using SessionManager
- Migrate all session-related APIs to use the new database schema
- Remove deprecated file-based session functions and replace with database operations
Reviewed Changes
Copilot reviewed 40 out of 42 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| ui/desktop/src/sessions.ts | Removes custom session management functions (updateSessionMetadata, deleteSession) |
| ui/desktop/src/goosed.ts | Adds error logging and removes server status check for external backend |
| ui/desktop/src/components/sessions/SessionsInsights.tsx | Updates to use new API session insights instead of custom fetch |
| ui/desktop/src/components/sessions/SessionListView.tsx | Migrates to use API functions for session operations |
| ui/desktop/src/api/types.gen.ts | Adds new API types for session insights and metadata operations |
| ui/desktop/src/api/sdk.gen.ts | Adds generated API functions for session management |
| crates/goose/src/session/session_manager.rs | New SQLite-based session manager implementation |
| crates/goose/src/session/legacy.rs | Legacy session file reader for migration purposes |
| crates/goose/src/agents/agent.rs | Updates agent code to use SessionManager instead of file operations |
| crates/goose-server/src/routes/session.rs | Updates server routes to use SessionManager |
| crates/goose-cli/src/session/mod.rs | Updates CLI session handling to use SessionManager |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| let storage = Arc::new(SessionStorage::new().await?); | ||
| match SESSION_STORAGE.set(storage.clone()) { | ||
| Ok(_) => Ok(storage), | ||
| Err(_) => Ok(SESSION_STORAGE.get().unwrap().clone()), |
There was a problem hiding this comment.
I think you do want to check the variants: https://docs.rs/tokio/latest/tokio/sync/enum.SetError.html#variants
There was a problem hiding this comment.
the LLMs said:
async fn instance() -> Result<Arc> {
let storage = Arc::new(SessionStorage::new().await?);
match SESSION_STORAGE.set(storage) {
Ok() => Ok(SESSION_STORAGE.get().unwrap().clone()),
Err() => Ok(SESSION_STORAGE.get().unwrap().clone()),
}
}
should do the trick, since failure means we already have the thing
There was a problem hiding this comment.
I think that's true for a regular OnceCell, but this is a tokio::OnceCell, which can fail to set if it's being concurrently set
If the OnceCell is empty, but some other task is currently trying to set the value, this call will fail with SetError::InitializingError.
| { | ||
| error!("Failed to persist compacted messages: {}", e); | ||
| } | ||
| tracing::info!("History replaced, compacting happened in reply"); |
There was a problem hiding this comment.
not in scope here, but have we considered how compaction should work with session history? seems like we should store the both the old and compacted state of things
There was a problem hiding this comment.
I think the idea is that we keep all messages around but annotate them with visibility. Hmm, is this already in /cc @katzdave
There was a problem hiding this comment.
Yes we have message metadata tagging messages appropriately as agent vs user visible.
I could see benefits of even more metadata tags like 'summary' being relevant.
I'm pushing this domain actively both in the UI for making the post/pre-summary messages more clear, and I have some other ideas around improving the summaries with with keeping around more user messages + messages at the end of tool call chains.
|
|
||
| session = SessionManager::get_session(&session.id, false) | ||
| .await | ||
| .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; |
There was a problem hiding this comment.
can we do something other than throw away the error here? maybe at minimum a log
There was a problem hiding this comment.
why particularly here? this should never fail I think - we're just refreshing the session after updating it
There was a problem hiding this comment.
maybe we leave it for another error handling PR. I just think returning 500s without error messages isn't great because there's always an extra step when debugging
crates/goose/src/conversation/mod.rs
Outdated
| .0 | ||
| .iter() | ||
| .zip(&other.0) | ||
| .all(|(a, b)| a.role == b.role && a.created == b.created && a.content == b.content) |
There was a problem hiding this comment.
should we not defer to equivalence of the Messages ?
There was a problem hiding this comment.
Pull Request Overview
Copilot reviewed 41 out of 43 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
crates/goose/src/agents/agent.rs:1
- This TODO indicates that database updates are missing when handling interruptions. This could lead to session state inconsistencies and should be addressed.
use std::collections::HashMap;
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| match SessionManager::create_session( | ||
| std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from(".")), | ||
| "Web session".to_string(), | ||
| ) |
There was a problem hiding this comment.
suggest something like
let session = SessionManager::create_session(..).await.map_err(..)?;
Redirect::permanent(..)
There was a problem hiding this comment.
hmm, also shouldn't be a permanent redirect I think
| ) | ||
| .await | ||
| .unwrap(); | ||
| Some(session.id) |
There was a problem hiding this comment.
this function should probably not panic or exit(1) but that's too much yak shaving
There was a problem hiding this comment.
this is the cli though so it is not the end of the world (well, for the cli it is) - but I agree since who knows who might thing it is a good idea to call this builder. pre-existing ...
crates/goose-cli/src/session/mod.rs
Outdated
| .await | ||
| .map(|session| session.conversation.unwrap_or_default()) | ||
| .unwrap_or_else(|e| { | ||
| eprintln!("Warning: Failed to load message history: {}", e); |
There was a problem hiding this comment.
I guess this isn't new but seems weird that this would just warn and continue
There was a problem hiding this comment.
so now you do want to panic? :)
| let storage = Arc::new(SessionStorage::new().await?); | ||
| match SESSION_STORAGE.set(storage.clone()) { | ||
| Ok(_) => Ok(storage), | ||
| Err(_) => Ok(SESSION_STORAGE.get().unwrap().clone()), |
There was a problem hiding this comment.
I think that's true for a regular OnceCell, but this is a tokio::OnceCell, which can fail to set if it's being concurrently set
If the OnceCell is empty, but some other task is currently trying to set the value, this call will fail with SetError::InitializingError.
| let current_version = self.get_schema_version().await?; | ||
|
|
||
| if current_version < CURRENT_SCHEMA_VERSION { | ||
| println!( |
There was a problem hiding this comment.
Perhaps we configure the logging utility so all output from these modules goes somewhere like:
/Users/alexhancock/.local/state/goose/logs/session-db
alongside the others?
There was a problem hiding this comment.
replaced the println!
| println!("Creating new session database..."); | ||
| let storage = Self::create(&db_path).await?; | ||
|
|
||
| if let Err(e) = storage.import_legacy(&session_dir).await { |
There was a problem hiding this comment.
Would be good to test this with a few real session dirs from the wild to ensure no issues before launch
There was a problem hiding this comment.
I imported 8000 from my folder...
| .await? | ||
| .unwrap_or(0); | ||
|
|
||
| let session_id = format!("{}_{}", today, max_idx + 1); |
There was a problem hiding this comment.
Do we ever use the fact that the ids have these dates in them? If not, could just be an autoincrementing int in sqlite.
| let modified_time = file_metadata.modified().unwrap_or(SystemTime::now()); | ||
| let created_time = file_metadata | ||
| .created() | ||
| .unwrap_or_else(|_| parse_session_timestamp(session_name).unwrap_or(modified_time)); |
There was a problem hiding this comment.
were the old session names human readable title strings? or is this a different field from the metadata and called session_name in this code
There was a problem hiding this comment.
nah, we just called it that. they were date strings. description is the human readable version
| s.id === sessionId | ||
| ? { ...s, metadata: { ...s.metadata, description: newDescription } } | ||
| : s | ||
| s.id === sessionId ? { ...s, metadata: { ...s, description: newDescription } } : s |
There was a problem hiding this comment.
do we need all the props at both root and metadata of this object? it seems like how we've flattened out the data model a lot this object could become flattened as well
There was a problem hiding this comment.
oops. there is no more metadata, this is just a remnant that will set this useless property that we then don't display until we get confirmation from the server. nice catch!
alexhancock
left a comment
There was a problem hiding this comment.
Looks like some conflicts but once resolved 🚢
yeah, with this type of thing, I'd rather let the conflicts vester than keep updating them |
* remove only-pr-labels (block#4842) Signed-off-by: Angela Ning <aning@squareup.com> * Docs: Add link to Plug & Play video for Reddit MCP (block#4852) * Fix: Token count UI doesn't re-render if it's open. (block#4822) * Update databricks flash model (block#4836) * Session manager (block#4648) Co-authored-by: Douwe Osinga <douwe@squareup.com> * Add Hacktoberfest Guides (block#4830) Co-authored-by: taniashiba <126204004+taniashiba@users.noreply.github.com> * docs: goose x Hacktoberfest 2025 Blog (block#4855) Co-authored-by: Tania Chakraborty <tchakraborty@block.xyz> Co-authored-by: Angie Jones <jones.angie@gmail.com> * fix: delete some flaky and not-useful tests (block#4861) * can tell the system what shell it is using (block#4807) * new subrecipe blog post banner (block#4862) * docs: remove recipe generator link from next to extension search (block#4858) * lowercase g in goose (block#4832) * doc: file parameter recipe update (block#4594) * Fiie input recipe ref doc (block#4869) * Add .goosehints file to enforce lowercase branding in documentation (block#4870) Co-authored-by: Angie Jones <jones.angie@gmail.com> * fix: restoring test data and correcting name (block#4875) * Dead code cleanup (block#4873) Co-authored-by: Douwe Osinga <douwe@squareup.com> Co-authored-by: Michael Neale <michael.neale@gmail.com> * Fix block#4612: Return non-zero exit code when CLI session ends with error (block#4621) Signed-off-by: jalateras <jima@comware.com.au> * Internal MCP Crate Cleanup (block#4800) * remove 2 redundant comments and one that lies (block#4866) Co-authored-by: Douwe Osinga <douwe@squareup.com> * Revert "Internal MCP Crate Cleanup (block#4800)" (block#4883) * fix(token_counter): fix panic with GitHub Copilot (block#4632) Signed-off-by: sings-to-bees-on-wednesdays <222684290+sings-to-bees-on-wednesdays@users.noreply.github.com> main was broken. this seems important * Cli web auth token (block#4456) Signed-off-by: Evanfeenstra <evanfeenstra@gmail.com> * make agent manager singleton (block#4880) * docs: new multi-model section with autopilot topic (block#4864) * docs: rename sub-recipe to subrecipe (block#4886) * alexhancock/mcp-crate-cleanup (block#4885) * Temporary workaround for mcp server * Add filtering for agentVisible: false messages on streaming providers (block#4847) * add new prompt to get all available tutorials (block#4802) Signed-off-by: AdemolaAri <ademola.ari@gmail.com> * Fix for auth in extension, fix for stale keychain * fix: path is duplicated on tool calls causing them to fail (block#4658) (block#4859) Signed-off-by: demetrio108 <demetrio108@protonmail.com> * Fix: LiteLLM API key field not showing in UI configuration (block#4105) Signed-off-by: jalateras <jima@comware.com.au> Co-authored-by: Ebony Louis <55366651+EbonyLouis@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Jack Amadeo <jackamadeo@squareup.com> * fix: Windows prompt cursor positioning issue with ANSI escape sequences (block#4464) Signed-off-by: Matt Donovan <mattddonovan@protonmail.com> Co-authored-by: Matt Donovan <mattddonovan@protonmail.com> * Allow better concurrent access (block#4896) Co-authored-by: Douwe Osinga <douwe@squareup.com> * feat(cli): add `path` & `limit` to `session list` command (block#4878) * Added CMD+T keyboard shortcut that takes you to the Home tab (block#4541) Signed-off-by: Guillaume Simard <2000390+GuiSim@users.noreply.github.com> --------- Signed-off-by: Angela Ning <aning@squareup.com> Signed-off-by: jalateras <jima@comware.com.au> Signed-off-by: Evanfeenstra <evanfeenstra@gmail.com> Signed-off-by: AdemolaAri <ademola.ari@gmail.com> Signed-off-by: demetrio108 <demetrio108@protonmail.com> Signed-off-by: Matt Donovan <mattddonovan@protonmail.com> Signed-off-by: Guillaume Simard <2000390+GuiSim@users.noreply.github.com> Co-authored-by: Angela Ning <32008323+angelahning@users.noreply.github.com> Co-authored-by: Emma Youndtsmith <90283317+emma-squared@users.noreply.github.com> Co-authored-by: David Katz <dkatz@squareup.com> Co-authored-by: Douwe Osinga <douwe@block.xyz> Co-authored-by: Douwe Osinga <douwe@squareup.com> Co-authored-by: Ebony Louis <55366651+EbonyLouis@users.noreply.github.com> Co-authored-by: taniashiba <126204004+taniashiba@users.noreply.github.com> Co-authored-by: taniandjerry <126204004+taniandjerry@users.noreply.github.com> Co-authored-by: Tania Chakraborty <tchakraborty@block.xyz> Co-authored-by: Angie Jones <jones.angie@gmail.com> Co-authored-by: Jack Amadeo <jackamadeo@block.xyz> Co-authored-by: Michael Neale <michael.neale@gmail.com> Co-authored-by: w. ian douglas <ian.douglas@iandouglas.com> Co-authored-by: Alex Hancock <alexhancock@block.xyz> Co-authored-by: Jarrod Sibbison <72240382+jsibbison-square@users.noreply.github.com> Co-authored-by: Rizel Scarlett <rizel@squareup.com> Co-authored-by: Jim Alateras <jima@comware.com.au> Co-authored-by: sings-to-bees-on-wednesdays <222684290+sings-to-bees-on-wednesdays@users.noreply.github.com> Co-authored-by: Evan Feenstra <evanfeenstra@gmail.com> Co-authored-by: Yingjie He <yingjiehe@squareup.com> Co-authored-by: dianed-square <73617011+dianed-square@users.noreply.github.com> Co-authored-by: Ademola Arigbabuwo <49918815+AdemolaAri@users.noreply.github.com> Co-authored-by: Demetrio ⚡️ <35406575+demetrio108@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Jack Amadeo <jackamadeo@squareup.com> Co-authored-by: Matt Donovan <mattddonovan@proton.me> Co-authored-by: Matt Donovan <mattddonovan@protonmail.com> Co-authored-by: Robert Mcgregor <38837341+exitcode0@users.noreply.github.com> Co-authored-by: Guillaume Simard <2000390+GuiSim@users.noreply.github.com>
Co-authored-by: Douwe Osinga <douwe@squareup.com> Signed-off-by: HikaruEgashira <hikaru-egashira@c-fo.com>
| let messages = if let Some(session_id) = &session_id { | ||
| tokio::task::block_in_place(|| { | ||
| tokio::runtime::Handle::current().block_on(async { | ||
| SessionManager::get_session(session_id, true) | ||
| .await | ||
| .map(|session| session.conversation.unwrap_or_default()) | ||
| .unwrap() | ||
| }) |
There was a problem hiding this comment.
Hi, @DOsinga! Since this got merged, I started seeing this error:
thread 'main' panicked at crates/goose-cli/src/session/mod.rs:136:26:
called `Result::unwrap()` on an `Err` value: Session not found
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
There was a problem hiding this comment.
Not sure if a good patch, but this fixed the issue for me locally: #5055
This bug got introduced in when merging dc29288 (block#4648). The problem was that the code was trying to load a session id from the database that does not exist.
This bug got introduced in when merging dc29288 (block#4648). The problem was that the code was trying to load a session id from the database that does not exist. Signed-off-by: Rodolfo Olivieri <rolivier@redhat.com>
This bug got introduced in when merging dc29288 (block#4648). The problem was that the code was trying to load a session id from the database that does not exist. Signed-off-by: Rodolfo Olivieri <rolivier@redhat.com>
Use SQLite for Session manager
Don't implement your own database